﻿
/**
 * @file ut_mathwrap.h
 * @brief 重新实现数学基础运算，满足跨平台要求
 * @author 杜涛(贺亚猛整理)
 * @version 0.1
 * @date 2022-01-19
 * @copyright Copyright 2022 Langge Software Co., Ltd. All rights reserved.
 */
#ifndef UT_MATH_WRAP_H
#define UT_MATH_WRAP_H

#include "ut_symbols.h"
#include <cmath>
#include <math.h>
#include <typeinfo>
#include <stdint.h>

BEGIN_SPACE_LANGE_VDR

// #ifdef PI
// #undef PI
// #endif // PI
// #define PI 3.14159265358979323846

/**
 * @brief 重新实现数学基础运算，满足跨平台要求
 * @author 杜涛(贺亚猛整理)
 */
class UT_API UTMath
{
public:
    static constexpr double PI = 3.14159265358979323846;
    static constexpr double PI_2 = 1.57079632679489661923;
    static constexpr double PI_4 = 0.78539816339744830962;
    static constexpr double LN_2 = 0.6914718055994530942;
    static constexpr double INVLN_2 = 1.0/LN_2;
    static constexpr double DOUBLE_EPS = 1e-8;
    static constexpr float FLOAT_EPS = (float)1e-6;
public:
    /**
     * @brief 计算指定值的绝对值
     * @tparam T  - 值类型
     * @param val - 指定值
     * @return 返回指定值的绝对值
     */
    template<typename T>
    static T Abs(const T val);

    /**
     * @brief 指定数值为nan
     * 
     * @tparam T 
     * @param val 
     * @return T 
     */
    template<typename T>
    static T SetNan(T & val);
    /**
     * @brief 检测指定数值是否为nan
     * @tparam T     - 值类型
     * @param  val   - 指定值 
     * @return true  - 如果为nan
     * @return false - 如果不为nan
     */
    template<typename T>
    static bool IsNan(const T & val);
    /**
     * @brief 进行两个值的比较
     * @tparam T     - 值类型
     * @param left   - 左值
     * @param right  - 右值
     * @return 返回值小于0时，说明left小于right
     *         返回值大于0时，说明left大于right
     *         返回值等于0时，说明left等于right
     */
    template<typename T>
    static int32_t Compare(const T& left,const T& right);
    /**
     * @brief 返回两者间较小的值
     * @tparam T     - 类型参数
     * @param left   - 左值
     * @param right  - 右值
     * @return 返回两者间较小的值
     */
    template<typename T>
    static T Min(const T left,const T right);
    /**
     * @brief 返回两者间较大的值
     * @tparam T     - 类型参数
     * @param left   - 左值
     * @param right  - 右值
     * @return 返回两者间较大的值
     */
    template<typename T>
    static T Max(const T left,const T right);
    /**
     * @brief 将值限定在不低于下限范围内
     * @tparam T          - 类型参数
     * @param  v          - 当前值
     * @param  minimum    - 下限值
     * @return 如果当前值小于下限值，则返回下限值
     *         其他情况，则返回自身值
     */
    template<typename T>
    static T ClampAbove(const T v,const T minimum);
    /**
     * @brief 将值限定在不高于上限范围内
     * @tparam T          - 类型参数
     * @param  v          - 当前值
     * @param  maximum    - 上限值
     * @return 如果当前值大于上限值，则返回上限值
     *         其他情况，则返回自身值
     */
    template<typename T>
    static T ClampBelow(const T v,const T maximum);
    /**
     * @brief 将值限制到上下限内
     * @tparam T        - 类型参数
     * @param  v        - 当前值  
     * @param minimum   - 下限值
     * @param maximum   - 上线值
     * @return 如果当前值小于下限，则返回下限值
     *         如果当前值大于上限，则返回上限值
     *         其他情况，则返回自身值
     */
    template<typename T>
    static T ClampBetween(const T v,const T minimum,const T maximum);
    /**
     * @brief 检测指定值的符号
     * @tparam T  -  类型参数
     * @param  v  -  指定值
     * @return 如果指定值大于等于零，则返回1，否则返回-1
     */
    template<typename T>
    static T Sign(const T v);
    /**
     * @brief 检测指定值的符号
     * @tparam T  -  类型参数
     * @param  v  -  指定值
     * @return v >  0 返回1
     *         v == 0 返回0
     *         v <  0 返回-1
     */
    template<typename T>
    static T SignOrZero(const T v);
    /**
     * @brief  计算指定值的平方
     * @tparam T   -  值类型
     * @param  v   -  指定值 
     * @return 返回指定值的平方
     */
    template<typename T>
    static T Square(const T v);
    /**
     * @brief  带符号的平方运算
     * @tparam  T - 值类型 
     * @param   v - 待运算值 
     * @return  如果v>=0,则返回v的平方
     *          如果v<0,则返回v的平方的负
     */
    template<typename T>
    static T SignedSquare(const T v);
    /**
     * @brief 定义指数为无符号整型类型的幂运算
     * @param base   底数
     * @param exp    指数
     * @return 返回运算结果
     */
    template<typename T>
    static T UIExpPower(const T base,const uint32_t exp);
public:
    /**
     * @brief 对指定值进行四舍五入取整
     * @param v - 输入值
     * @return 如果v>=0 使用floorf(v+0.5f)实现
     *         如果v<0  使用ceilf(v-0.5f)实现
     */
    static float Round(const float v);
    /**
     * @brief 对指定值进行四舍五入取整
     * @param v - 输入值
     * @return 如果v>=0 使用floor(v+0.5)实现
     *         如果v<0  使用ceil(v-0.5)实现
     */
    static double Round(const double v);
    /**
     * @brief 将角度转化为弧度
     * @param degrees  -  角度
     * @return 返回转换后的弧度
     */
    static float DegreesToRadians(const float degrees);
    /**
     * @brief 将角度转换为弧度
     * @param degrees    角度
     * @return 返回转换后的弧度
     */
    static double DegreesToRadians(const double degrees);
    /**
     * @brief 将弧度转换为角度
     * @param rad - 输入弧度
     * @return 返回转换后的角度
     */
    static float RadiansToDegrees(const float rad);
    /**
     * @brief 将弧度转换为角度
     * @param rad - 输入弧度
     * @return 返回转换后的角度
     */
    static double RadiansToDegrees(const double rad);
    /**
     * @brief 转换uint32_t字节顺序,高低互换
     * @param nSrc    转换前的uint32_t数
     * @return 转换后的uint32_t数
     */
    static uint32_t ReserveDWORD(const uint32_t nSrc);
    /**
     * @brief 转换uint16_t字节顺序,高低互换
     * @param nSrc   转换前的uint16_t数
     * @return 转换后的uint16_t数
     */
    static uint16_t ReserveWORD(const uint16_t nSrc);
    /**
     * @brief 查表法计算正弦（此函数中，全部按输入角度乘以1024计算）
     * @param angle     角度
     * @return 返回计算结果
     */
    static double SinTimes1024(int32_t angle);
    /**
     * @brief 查表法计算余弦（此函数中，全部按输入角度乘以1024计算）
     * @param angle     角度
     * @return 返回计算结果
     */
    static double CosTimes1024(int32_t angle);
    /**
     * @brief 查表法计算反余弦（此函数中，全部按输入角度乘以1024计算）
     * @param value    输入值
     * @return 返回计算结果
     */
    static int32_t AcosTimes1024(double value);
    /**
     * @brief 查表法计算反正弦（此函数中，全部按输入角度乘以1024计算）
     * @param value    输入值
     * @return 返回计算结果
     */
    static int32_t AsinTimes1024(double value);
    /**
     * @brief 查表法实现正切（此函数中，全部按输入角度乘以1024计算）
     * @param angle    角度
     * @return 返回计算结果
     */
    static double TanTimes1024(int32_t angle);
    /**
     * @brief 查表法计算反正切（此函数中，全部按输入角度乘以1024计算）
     * @param value_y    y分量
     * @param value_x    x分量
     * @return 返沪计算结果
     */
    static int32_t Atan2Times1024(double value_y,double value_x);
    /**
     * @brief 查表法三角函数sin
     * @param angle   角度[in]
     * @return sin结果
     */
    static double SIN(const int32_t angle);
    /**
     * @brief 查表法 三角函数cos
     * @param angle   角度[in]
     * @return cos结果
     */
    static double COS(const int32_t angle);
    /**
     * @brief 查表法 三角函数tan
     * @param angle     角度[in]
     * @return tan结果 
     */
    static double TAN(const int32_t angle);
    /**
     * @brief 查表法 三角函数cot
     * @param angle     角度[in]
     * @return cot结果
     */
    static double COT(const int32_t angle);
    /**
     * @brief 开方运算
     * @param n  输入值
     * @return 返回计算结果 
     */
    static uint32_t SQRT(const int32_t n);
private:
    UTMath()=delete;
    UTMath(UTMath &&)=delete;
    UTMath(const UTMath &)=delete;
    UTMath &operator=(UTMath &&)=delete;
    UTMath &operator=(const UTMath &)=delete;
};

/**
 * @brief 计算指定值的绝对值
 * @tparam T  - 值类型
 * @param val - 指定值
 * @return 返回指定值的绝对值
 */
template<typename T>
T UTMath::Abs(const T val)
{
    return val<static_cast<T>(0)?-val:val;
}

/**
 * @brief 检测指定数值是否为nan
 * @tparam T     - 值类型
 * @param  val   - 指定值 
 * @return true  - 如果为nan
 * @return false - 如果不为nan
 */
template<typename T>
bool UTMath::IsNan(const T & val)
{
//    if(sizeof(T) <= sizeof(float))
//        return isnan<float>(val);
//    else if(sizeof(T) <= sizeof(double))
//        return isnan<double>(val);
//    else
//        return true;
    return false;
}

/**
 * @brief 进行两个值的比较
 * @tparam T     - 值类型
 * @param left   - 左值
 * @param right  - 右值
 * @return 返回值小于0时，说明left小于right
 *         返回值大于0时，说明left大于right
 *         返回值等于0时，说明left等于right
 */
template<typename T>
int32_t UTMath::Compare(const T &left,const T &right)
{
    if(IsNan(left))
        return -1;
    else if(IsNan(right))
        return 1;
    if(typeid(T)==typeid(float)||typeid(T)==typeid(double))
    {
        if(typeid(T)==typeid(float))
        {
            if(std::fabs((float)(right-left))<FLOAT_EPS)
            {
                return 0;
            }
        }
        else if(std::fabs(right-left)<DOUBLE_EPS)
        {
            return 0;
        }
        if(left<right)
        {
            return -1;
        }
        else
        {
            return 1;
        }
    }
    else
    {
        return (left==right)?0:((left<right)?-1:1);
    }
}

/**
 * @brief 返回两者间较小的值
 * @tparam T     - 类型参数
 * @param left   - 左值
 * @param right  - 右值
 * @return 返回两者间较小的值
 */
template<typename T>
T UTMath::Min(const T left,const T right)
{
    return Compare(left,right)<0?left:right;
}

/**
 * @brief 返回两者间较大的值
 * @tparam T     - 类型参数
 * @param left   - 左值
 * @param right  - 右值
 * @return 返回两者间较大的值
 */
template<typename T>
T UTMath::Max(const T left,const T right)
{
    return Compare(left,right)>0?left:right; 
}

/**
 * @brief 将值限定在不低于下限范围内
 * @tparam T          - 类型参数
 * @param  v          - 当前值
 * @param  minimum    - 下限值
 * @return 如果当前值小于下限值，则返回下限值
 *         其他情况，则返回自身值
 */
template<typename T>
T UTMath::ClampAbove(const T v,const T minimum)
{ 
    return Compare(v,minimum)<0?minimum:v;
}

/**
 * @brief 将值限定在不高于上限范围内
 * @tparam T          - 类型参数
 * @param  v          - 当前值
 * @param  maximum    - 上限值
 * @return 如果当前值大于上限值，则返回上限值
 *         其他情况，则返回自身值
 */
template<typename T>
T UTMath::ClampBelow(const T v,const T maximum) 
{
    return Compare(v,maximum)>0?maximum:v; 
}

/**
 * @brief 将值限制到上下限内
 * @tparam T        - 类型参数
 * @param  v        - 当前值  
 * @param minimum   - 下限值
 * @param maximum   - 上线值
 * @return 如果当前值小于下限，则返回下限值
 *         如果当前值大于上限，则返回上限值
 *         其他情况，则返回自身值
 */
template<typename T>
T UTMath::ClampBetween(const T v,const T minimum,const T maximum)
{ 
    return ClampBelow(ClampAbove(v,minimum),maximum); 
}

/**
 * @brief 检测指定值的符号
 * @tparam T  -  类型参数
 * @param  v  -  指定值
 * @return 如果指定值大于等于零，则返回1，否则返回-1
 */
template<typename T>
T UTMath::Sign(const T v)
{ 
    return v<static_cast<T>(0)?static_cast<T>(-1):
        static_cast<T>(1); 
}

/**
 * @brief 检测指定值的符号
 * @tparam T  -  类型参数
 * @param  v  -  指定值
 * @return v >  0 返回1
 *         v == 0 返回0
 *         v <  0 返回-1
 */
template<typename T>
T UTMath::SignOrZero(const T v)
{ 
    return v<static_cast<T>(0)?static_cast<T>(-1):
        (v>static_cast<T>(0)?static_cast<T>(1):static_cast<T>(0)); 
}

/**
 * @brief  计算指定值的平方
 * @tparam T   -  值类型
 * @param  v   -  指定值 
 * @return 返回指定值的平方
 */
template<typename T>
T UTMath::Square(const T v)
{ 
    return v*v; 
}

/**
 * @brief  带符号的平方运算
 * @tparam  T - 值类型 
 * @param   v - 待运算值 
 * @return  如果v>=0,则返回v的平方
 *          如果v<0,则返回v的平方的负
 */
template<typename T>
T UTMath::SignedSquare(const T v)
{ 
    return v<static_cast<T>(0)?-v*v:v*v;
}

/**
 * @brief 定义指数为无符号整型类型的幂运算
 * @param base   底数
 * @param exp    指数
 * @return 返回运算结果
 */
template<typename T>
T UTMath::UIExpPower(const T base,const uint32_t exp)
{
    T result=static_cast<T>(1);
    for (uint32_t i=0;i<exp;++i)
    {
        result*=base;
    }
    return result;
}

END_SPACE_LANGE_VDR

#endif // !UT_MATH_WRAP_H